#include <stdio.h>
#include<ctype.h>
#include<string.h>
#include <gst/gst.h>
#include <glib.h>
#include <math.h>
#include <unistd.h>


GMainLoop *loop;
GstElement *pipeline, *source, *mux, *encoder, *conv, *sink, *level;
GstBus *bus;
GIOChannel*input;
gint watch_id;

//Function for Adding an Observer to Input Output Channel
static gboolean observer (GIOChannel* channel, GIOCondition condition, gpointer data)
{


        gchar buffer[256];
        int bytes_read;
        gint fd;
        //gint ret;

        fd = g_io_channel_unix_get_fd(channel);
        bytes_read = read(fd, buffer, 256);
        buffer[bytes_read-1] = '\0';
        //g_print("command:: %s\n", buffer);
       
        if((strcmp(buffer,"q"))==0)
        {
                gst_element_send_event (pipeline, gst_event_new_eos ());
                sleep(1);
                g_print ("Returned, stopping recording\n");
                gst_element_set_state (pipeline, GST_STATE_NULL);
                g_print ("Deleting pipeline\n");
                gst_object_unref (GST_OBJECT (pipeline));
                exit(1);
        }
	return 1;
}

//Function for adding an Observer for Bus
static gboolean bus_call (GstBus *bus,GstMessage *msg,gpointer data)
{
        GMainLoop *loop = (GMainLoop *) data;
        switch (GST_MESSAGE_TYPE (msg))
        {
                case GST_MESSAGE_EOS:
                        g_print ("End of stream\n");
                        g_main_loop_quit (loop);
                        break;
                case GST_MESSAGE_ERROR:
                {
                        gchar *debug;
                        GError *error;
                        gst_message_parse_error (msg, &error, &debug);
                        g_free (debug);
                        g_printerr ("Error: %s\n", error->message);
                        g_error_free (error);
                        g_main_loop_quit (loop);
                        break;
                }
                default:
                        break;
        }
	if (msg->type == GST_MESSAGE_ELEMENT) 
	{
	    	const GstStructure *s = gst_message_get_structure (msg);
	    	const gchar *name = gst_structure_get_name (s);

	    	if (strcmp (name, "level") == 0) 
		{
		      	gint channels;
		      	GstClockTime endtime;
		      	gdouble rms_dB, peak_dB, decay_dB;
		      	gdouble rms;
		      	const GValue *list;
		      	const GValue *value;
		
		      	gint i;
		
		      	if (!gst_structure_get_clock_time (s, "endtime", &endtime))
			        g_warning ("Could not parse endtime");
		      	/* we can get the number of channels as the length of any of the value
		       	* lists */
		      	list = gst_structure_get_value (s, "rms");
		      	channels = gst_value_list_get_size (list);
		
		      	//g_print ("endtime: %" GST_TIME_FORMAT ", channels: %d\n",GST_TIME_ARGS (endtime), channels);
		      	for (i = 0; i < channels; ++i) 
			{
		        	//g_print ("channel %d\n", i);
			        list = gst_structure_get_value (s, "rms");
			        value = gst_value_list_get_value (list, i);
			        rms_dB = g_value_get_double (value);
			        list = gst_structure_get_value (s, "peak");
			        value = gst_value_list_get_value (list, i);
			        peak_dB = g_value_get_double (value);
			        list = gst_structure_get_value (s, "decay");
			        value = gst_value_list_get_value (list, i);
			        decay_dB = g_value_get_double (value);
			        g_print ("    \"RMS\": \"%f dB\", \"peak\": \"%f dB\", \"decay\": \"%f dB\"\n",rms_dB, peak_dB, decay_dB);
		
			        /* converting from dB to normal gives us a value between 0.0 an	d 1.0 */
		        	rms = pow (10, rms_dB / 20);
		        	//g_print ("    \"normalized rms value\": \"%f\"\n", rms);
		      	}
		}
	        
	}
	return TRUE;
}
/*static void on_pad_added (GstElement *element,GstPad *pad,gpointer data)
{
        GstPad *sinkpad;
        GstElement *decoder = (GstElement *) data;
         We can now link this pad with the vorbis-decoder sink pad 
        g_print ("Dynamic pad created, linking demuxer/decoder\n");
        sinkpad = gst_element_get_static_pad (decoder, "sink");
        gst_pad_link (pad, sinkpad);
        gst_object_unref (sinkpad);
}
static gboolean message_handler (GstBus * bus, GstMessage * message, gpointer data)
{

  if (message->type == GST_MESSAGE_ELEMENT) {
    const GstStructure *s = gst_message_get_structure (message);
    const gchar *name = gst_structure_get_name (s);

    if (strcmp (name, "level") == 0) {
      gint channels;
      GstClockTime endtime;
      gdouble rms_dB, peak_dB, decay_dB;
      gdouble rms;
      const GValue *list;
      const GValue *value;

      gint i;

      if (!gst_structure_get_clock_time (s, "endtime", &endtime))
        g_warning ("Could not parse endtime");
       //we can get the number of channels as the length of any of the value
       // lists 
      list = gst_structure_get_value (s, "rms");
      channels = gst_value_list_get_size (list);

      g_print ("endtime: %" GST_TIME_FORMAT ", channels: %d\n",
          GST_TIME_ARGS (endtime), channels);
      for (i = 0; i < channels; ++i) {
        g_print ("channel %d\n", i);
        list = gst_structure_get_value (s, "rms");
        value = gst_value_list_get_value (list, i);
        rms_dB = g_value_get_double (value);
        list = gst_structure_get_value (s, "peak");
        value = gst_value_list_get_value (list, i);
        peak_dB = g_value_get_double (value);
        list = gst_structure_get_value (s, "decay");
        value = gst_value_list_get_value (list, i);
        decay_dB = g_value_get_double (value);
        g_print ("    RMS: %f dB, peak: %f dB, decay: %f dB\n",
            rms_dB, peak_dB, decay_dB);

        // converting from dB to normal gives us a value between 0.0 and 1.0 
        rms = pow (10, rms_dB / 20);
        g_print ("    normalized rms value: %f\n", rms);
      }
    }
  }
  // we handled the message we want, and ignored the ones we didn't want.
   // so the core can unref the message for us 
  return TRUE;
}*/
int main (int argc,char *argv[])
{
        //FILE *a;
        int choice;
       
        /* Initialisation */
        gst_init (&argc, &argv);
        loop = g_main_loop_new (NULL, FALSE);
        if (argc == 2)
        {
                choice=1;
        }
        else if(argc ==5)
        {
                choice=2;
        }
        else
        {
                g_printerr ("\n\nFor Recording to file Use: %s <Ogg/Vorbis filename>.\n", argv[0]);
                g_printerr ("\nAnd for Recording and streaming to Icecast server use: %s <Server Address> <Port Address> <Password> <Mount Point> \n", argv[0]);
                choice=3;
        }
        switch (choice)
        {
                case 1:
                        //printf("You Choose Menu Item 1\n");
                                       
                        /* Check input arguments */
                       
                        pipeline = gst_pipeline_new ("audio-player");
                        source = gst_element_factory_make ("alsasrc", "source");
                        mux = gst_element_factory_make ("oggmux", "ogg-mux");
                        encoder = gst_element_factory_make ("vorbisenc", "vorbis-encoder");
                        conv = gst_element_factory_make ("audioconvert", "converter");
                        sink = gst_element_factory_make ("filesink", "file-output");
       			level = gst_element_factory_make ("level", NULL);
  			//g_assert (level);
                        if (!pipeline || !source || !mux || !encoder || !conv || !sink)
                        {
                                g_printerr ("One element could not be created. Exiting.\n");
                                return -1;
                        }
                       
                        g_object_set (G_OBJECT (source), "device", "plughw:0", NULL);
                        g_object_set (G_OBJECT (sink), "location", argv[1], NULL);
       
                        bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline));
                        gst_bus_add_watch (bus, bus_call, loop);
                        gst_object_unref (bus);
       
                        gst_bin_add_many (GST_BIN (pipeline),source, conv, encoder,level, mux,sink, NULL);
                        gst_element_link (source, conv);
                        gst_element_link (conv,level);
                        gst_element_link (level,encoder);
                        gst_element_link (encoder,mux);
                        gst_element_link (mux,sink);
       			g_object_set (G_OBJECT (level), "message", TRUE, NULL);
			g_object_set (G_OBJECT (sink), "sync", TRUE, NULL);

			/*bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline));
			watch_id = gst_bus_add_watch (bus, message_handler, NULL);
                        gst_object_unref(bus);*/

			//g_print ("Now Recording: %s\n", file);
                        gst_element_set_state (pipeline, GST_STATE_PLAYING);
       
                        //g_print ("Recording...\n");
                       
                        input = g_io_channel_unix_new (fileno(stdin));
                        g_io_add_watch(input , G_IO_IN, observer, NULL);
                       
                        g_main_loop_run (loop);
       
                        break;
                case 2:
                       
                        printf("Your file %s ready to record and to stream to %s through %s\n", argv[4],argv[1],argv[2]);
                        pipeline = gst_pipeline_new ("audio-player");
                        source = gst_element_factory_make ("alsasrc", "source");
                        mux = gst_element_factory_make ("oggmux", "ogg-mux");
                        encoder = gst_element_factory_make ("vorbisenc", "vorbis-encoder");
                        conv = gst_element_factory_make ("audioconvert", "converter");
                        sink = gst_element_factory_make ("shout2send", "output-server");
       
                        if (!pipeline || !source || !mux || !encoder || !conv || !sink)
                        {
                        g_printerr ("One element could not be created. Exiting.\n");
                        return -1;
                        }
                       
                        g_object_set (G_OBJECT (source), "device", "plughw:0", NULL);
                        g_object_set (G_OBJECT (sink), "mount", argv[4], NULL);
                        g_object_set (G_OBJECT (sink), "ip", argv[1], NULL);
                        g_object_set (G_OBJECT (sink), "port", 8000, NULL);
                        g_object_set (G_OBJECT (sink), "password", argv[3], NULL);
                       
                                                                       
                        bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline));
                        gst_bus_add_watch (bus, bus_call, loop);
                        gst_object_unref (bus);
               
                        gst_bin_add_many (GST_BIN (pipeline),source, conv, encoder, mux,                                        sink, NULL);
                        gst_element_link (source, conv);
                        gst_element_link (conv,encoder);
                        gst_element_link (encoder,mux);
                        gst_element_link (mux,sink);
               
                        //g_print ("Now Recording: %s\n", file);
                        gst_element_set_state (pipeline, GST_STATE_PLAYING);
                        //g_print ("Recording...\n");
                       
                        input = g_io_channel_unix_new (fileno(stdin));
                        g_io_add_watch(input , G_IO_IN, observer, NULL);
                               
                        g_main_loop_run (loop);
                               
                        break;
                case 3:
                        exit(1);
                        break;
                       
                default:
                        printf("Enter Correct Option :-)\n");
                               
        }
               
                               
        return 0;
}              


